package com.atguigu.gmall.order.service.imp;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.atguigu.gmall.product.client.common.constant.RedisConst;
import com.atguigu.gmall.product.client.common.util.HttpClientUtil;
import com.atguigu.gmall.product.client.model.enums.OrderStatus;
import com.atguigu.gmall.product.client.model.enums.ProcessStatus;
import com.atguigu.gmall.product.client.model.order.OrderDetail;
import com.atguigu.gmall.product.client.model.order.OrderInfo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class OrderInfoServiceImpl implements OrderInfoService {


    @Value("${ware.url}")
    private String wareUrl;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    //提交订单
    @Override
    public Long submitOrder(OrderInfo orderInfo) {

        //1:订单表
        //将订单的商品清单 根据skuId 重新加载一次
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        StringBuilder sb = new StringBuilder();
        orderDetailList.forEach(orderDetail -> {
            //防止页面被人篡改  重新查询价格
            BigDecimal price = productFeignClient.getPrice(orderDetail.getSkuId());
            orderDetail.setOrderPrice(price);
            //订单描述
            sb.append(orderDetail.getSkuName() + " ");
        });
        //1.1 总金额  重新订算
        orderInfo.sumTotalAmount();
        //1.2订单状态
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        //1.3 订单交易编号  (第三方）
        //必须唯一  ： 1ms 里下了多张订单  同一个交易编号  不唯一
        String outTradeNo = "ATGUIGU" + System.currentTimeMillis() +
                new Random().nextInt(1000);
        orderInfo.setOutTradeNo(outTradeNo);
        //1.4 订单描述
        if(null != sb && sb.length() > 100){
            //截取
            orderInfo.setTradeBody(sb.toString().substring(0,100));
        }else{
            orderInfo.setTradeBody(sb.toString());
        }
        //1.5 创建时间
        //Date curDate = new Date();
        Calendar calendar = Calendar.getInstance();
        //当前时间
        orderInfo.setCreateTime(calendar.getTime());
        //1.6 失效时间  2小时 半小时
        calendar.add(Calendar.MINUTE,30);
        //半小时之后的时间
        orderInfo.setExpireTime(calendar.getTime());
        //1.7 进度状态
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        //1.8图片路径
        //orderInfo.setImgUrl();
        orderInfoMapper.insert(orderInfo);
        //2:订单详情表  （商品清单）
        List<OrderDetail> orderDetailList1 = orderInfo.getOrderDetailList();
        List<String> skuIds = new ArrayList<>();

        orderDetailList1.forEach(orderDetail -> {
            //订单表的主键
            orderDetail.setOrderId(orderInfo.getId());
            skuIds.add(orderDetail.getSkuId().toString());
            orderDetailMapper.insert(orderDetail);

        });
        //1）将已经生成订单的商品从购物车中删除出去  canal
        String cacheH = RedisConst.USER_KEY_PREFIX + orderInfo.getUserId()
                + RedisConst.USER_CART_KEY_SUFFIX;
        redisTemplate.opsForHash().delete(cacheH,skuIds.toArray());
        //2）删除DB 同学们完成

        // 取消订单   ： 提交订单开始计时 30分钟后 如果用户不支付 取消订单
        // 12306 买票  30分钟    锁定的时间
        // 查询历史订单  取消   支付 未支付
        // 定时器 （下下策）
        // （上上策）  MQ  延迟消息  30分钟之后马上发消息  执行订单的取消

        //发送延迟消息  插件方式
        rabbitService.sendDelayMessage(
                MqConst.EXCHANGE_DIRECT_ORDER_CANCEL,  //交换机
                MqConst.ROUTING_ORDER_CANCEL,   //routingKey
                orderInfo.getId(),          //发送的消息
                MqConst.DELAY_TIME      //延迟时间
        );



        return orderInfo.getId();
    }
        @Autowired
        private RabbitService rabbitService;

    //校验库存  连接仓库
    @Override
    public boolean hasStock(Long skuId, Integer skuNum) {
        String url = wareUrl + "/hasStock?skuId="+skuId+"&num="+skuNum;
        return "1".equals(HttpClientUtil.doGet(url));
    }

    //1 获取订单状态是否未支付
    //2 是否支付更新订单状态为已关闭
    //取消订单
    @Override
    public void cancelOrder(Long orderId) {
        //1 获取订单状态是否未支付
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (orderInfo != null){
            if (orderInfo.getOrderStatus().equals(OrderStatus.UNPAID.name())){
                //2 是否支付更新订单状态为已关闭
                orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
                //进度状态也改成已关闭
                orderInfo.setProcessStatus(ProcessStatus.CLOSED.name());

                System.out.println("取消订单成功" + new Date());
                //更新订单
                orderInfoMapper.updateById(orderInfo);
            }
        }
    }

    //根据订单Id 查询订单对象  获取订单信息
    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        //订单详情
        orderInfo.setOrderDetailList(orderDetailMapper.selectList(new QueryWrapper<OrderDetail>()
        .eq("order_id",orderId)));
        return orderInfo;
    }

    //更新订单
    @Override
    public void updateOrderStatus(Long orderId,ProcessStatus processStatus) {
                //查询
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setId(orderId);
            //更新订单状态
            orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
            //更新进度状态
            orderInfo.setProcessStatus(processStatus.name());

            //更新
            orderInfoMapper.updateById(orderInfo);
    }

    //发送消息   通知仓库  减库存
    @Override
    public void sendOrderStatus(Long orderId) {
        //1. 更新订单状态  为已通知仓储
        updateOrderStatus(orderId,ProcessStatus.NOTIFIED_WARE);
        //2. 查询订单对象
        OrderInfo orderInfo = getOrderInfo(orderId);
        //3. 组织数据
        Map<String, Object> resultMap = initWareOrder(orderInfo);
        //4. 通知仓库
        rabbitService.sendMessage(
                MqConst.EXCHANGE_DIRECT_WARE_STOCK,
                MqConst.ROUTING_WARE_STOCK,
                JSONObject.toJSONString(resultMap));
    }


    //构建发给仓库所需要的数据
    public Map<String,Object> initWareOrder(OrderInfo orderInfo){
        Map<String,Object> resultMap = new HashMap<>();
        //收获订单Id
        resultMap.put("orderId",orderInfo.getId());
        //收获人
        resultMap.put("consignee",orderInfo.getConsignee());
        //收获人手机号
        resultMap.put("consigneeTel",orderInfo.getConsigneeTel());
        //订单备注
        resultMap.put("orderComment",orderInfo.getOrderComment());
        //订单概要
        resultMap.put("orderBody",orderInfo.getTradeBody());
        //发送地址
        resultMap.put("deliveryAddress",orderInfo.getDeliveryAddress());
        //支付方式
        resultMap.put("paymentWay","ONLINE".equals(orderInfo.getPaymentWay())?"2":"1");

        //订单详情
        List<Map> details = orderInfo.getOrderDetailList().stream().map(orderDetail -> {
            Map map = new HashMap<>();
            map.put("skuId", orderDetail.getSkuId());
            map.put("skuNum", orderDetail.getSkuNum());
            map.put("skuName", orderDetail.getSkuName());
            return map;
        }).collect(Collectors.toList());
        resultMap.put("details",details);
        //传入仓库Id
        resultMap.put("wareId",orderInfo.getWareId());

        return resultMap;
    }

    //开始拆单
    @Override
    public List<OrderInfo> orderSplit(Long orderId, String wareSkuMap) {
        //1张订单拆分成多张订单 1张订单中有多个订单详情  由于商品不放在一个仓库 以订单详情来进行拆单
        //  注意1 : 订单表(原订单数据不能删除  直接产生多个子订单即可 子订单依赖于原订单)
        //      1. 原订单的订单状态改为已拆单
        //      2. 子订单状态改为已支付 (沿用原订单的订单状态)  子订单的父订单Id使用原订单的Id
        //  注意2 : 订单详情表  外键订单Id改为子订单Id

        //前提  转成ListMap
        List<Map> mapList = JSONObject.parseArray(wareSkuMap, Map.class);
        //根据原订单id获取原订单信息
        OrderInfo orderInfoOrigin = getOrderInfo(orderId);
        //子订单集合
        List<OrderInfo> subOrderInfoList = mapList.stream().map(map -> {
            //遍历仓库  每一个仓库就是map
            //一个仓库一个订单
            //子订单数据来源于父订单
            OrderInfo subOrderInfo = new OrderInfo();
            BeanUtils.copyProperties(orderInfoOrigin,subOrderInfo);

            //解决问题:
            //1) 设置子订单的Id为null
            subOrderInfo.setId(null);
            //2) 设置父Id
            subOrderInfo.setParentOrderId(orderInfoOrigin.getId());
            // 保存子订单的订单详情
            List<OrderDetail> subOrderDetailList =new ArrayList<>();
            List<String> skuIds = (List<String>) map.get("skuIds");
            for (String skuId : skuIds) {
                //  根据skuId + 父Id
                //原始订单详情集合
                List<OrderDetail> orderDetailListOrigin = orderInfoOrigin.getOrderDetailList();
                for (OrderDetail orderDetailOrigin : orderDetailListOrigin) {
                    if (orderDetailOrigin.getSkuId().toString().equals(skuId)){
                        subOrderDetailList.add(orderDetailOrigin);
                        break;
                    }
                }
            }
            //3) 设置子订单的订单详情的引用
            subOrderInfo.setOrderDetailList(subOrderDetailList);
            //计算总金额
            subOrderInfo.sumTotalAmount();
            //保存子订单
            orderInfoMapper.insert(subOrderInfo);
            //更新当前子订单对应的多个订单详情(设置外键)
            subOrderInfo.getOrderDetailList().forEach(orderDetail -> {
                orderDetail.setOrderId(subOrderInfo.getId());
                orderDetailMapper.updateById(orderDetail);
            });
            return subOrderInfo;
        }).collect(Collectors.toList());
        //更新原订单的订单状态 及进度状态
        updateOrderStatus(orderId,ProcessStatus.SPLIT);

        return subOrderInfoList;
    }

    //对上面方法的优化
    @Override
    public List<Map> orderSplit1(Long orderId, String wareSkuMap) {
        //1张订单拆分成多张订单 1张订单中有多个订单详情  由于商品不放在一个仓库 以订单详情来进行拆单
        //  注意1 : 订单表(原订单数据不能删除  直接产生多个子订单即可 子订单依赖于原订单)
        //      1. 原订单的订单状态改为已拆单
        //      2. 子订单状态改为已支付 (沿用原订单的订单状态)  子订单的父订单Id使用原订单的Id
        //  注意2 : 订单详情表  外键订单Id改为子订单Id

        //前提  转成ListMap
        List<Map> mapList = JSONObject.parseArray(wareSkuMap, Map.class);
        //根据原订单id获取原订单信息
        OrderInfo orderInfoOrigin = getOrderInfo(orderId);

        List<Map> listMap = mapList.stream().map(map -> {
            //遍历仓库  每一个仓库就是map
            //一个仓库一个订单
            //子订单数据来源于父订单
            OrderInfo subOrderInfo = new OrderInfo();
            BeanUtils.copyProperties(orderInfoOrigin,subOrderInfo);

            //解决问题:
            //1) 设置子订单的Id为null
            subOrderInfo.setId(null);
            //2) 设置父Id
            subOrderInfo.setParentOrderId(orderInfoOrigin.getId());
            //3) 记录当前子订单是哪个仓库
            subOrderInfo.setWareId(String.valueOf(map.get("wareId")));
            // 保存子订单的订单详情
            List<String> skuIds = (List<String>) map.get("skuIds");

            List<OrderDetail> subOrderDetailList =new ArrayList<>();
            for (String skuId : skuIds) {
                //  根据skuId + 父Id
                //原始订单详情集合
                List<OrderDetail> orderDetailListOrigin = orderInfoOrigin.getOrderDetailList();
                for (OrderDetail orderDetailOrigin : orderDetailListOrigin) {
                    if (orderDetailOrigin.getSkuId().toString().equals(skuId)){
                        subOrderDetailList.add(orderDetailOrigin);
                        break;
                    }
                }
            }
            //3) 设置子订单的订单详情的引用
            subOrderInfo.setOrderDetailList(subOrderDetailList);
            //计算总金额
            subOrderInfo.sumTotalAmount();
            //保存子订单
            orderInfoMapper.insert(subOrderInfo);
            //更新当前子订单对应的多个订单详情(设置外键)
            subOrderInfo.getOrderDetailList().forEach(orderDetail -> {
                orderDetail.setOrderId(subOrderInfo.getId());
                orderDetailMapper.updateById(orderDetail);
            });
            return initWareOrder(subOrderInfo);
        }).collect(Collectors.toList());

        return mapList;
    }

}